home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / cmds / more / more.c < prev    next >
C/C++ Source or Header  |  1989-12-10  |  36KB  |  1,833 lines

  1. /*
  2.  * Copyright (c) 1980 Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms are permitted
  6.  * provided that this notice is preserved and that due credit is given
  7.  * to the University of California at Berkeley. The name of the University
  8.  * may not be used to endorse or promote products derived from this
  9.  * software without specific written prior permission. This software
  10.  * is provided ``as is'' without express or implied warranty.
  11.  */
  12.  
  13. #ifndef lint
  14. char copyright[] =
  15. "@(#) Copyright (c) 1980 Regents of the University of California.\n\
  16.  All rights reserved.\n";
  17. #endif /* not lint */
  18.  
  19. #ifndef lint
  20. static char sccsid[] = "@(#)more.c    5.18 (Berkeley) 4/25/88";
  21. #endif /* not lint */
  22.  
  23. /*
  24. ** more.c - General purpose tty output filter and file perusal program
  25. **
  26. **    by Eric Shienbrood, UC Berkeley
  27. **
  28. **    modified by Geoff Peck, UCB to add underlining, single spacing
  29. **    modified by John Foderaro, UCB to add -c and MORE environment variable
  30. */
  31.  
  32. #include <stdio.h>
  33. #include <sys/param.h>
  34. #include <ctype.h>
  35. #include <signal.h>
  36. #include <errno.h>
  37. #include <sgtty.h>
  38. #include <setjmp.h>
  39. #include <sys/stat.h>
  40. #include <sys/file.h>
  41. #include <a.out.h>
  42. #include <varargs.h>
  43.  
  44. #define HELPFILE    "/sprite/lib/more/more.help"
  45. #define VI        "/sprite/cmds/vi"
  46.  
  47. #define Fopen(s,m)    (Currline = 0,file_pos=0,fopen(s,m))
  48. #define Ftell(f)    file_pos
  49. #define Fseek(f,off)    (file_pos=off,fseek(f,off,0))
  50. #define Getc(f)        (++file_pos, getc(f))
  51. #define Ungetc(c,f)    (--file_pos, ungetc(c,f))
  52.  
  53. #define MBIT    CBREAK
  54. #define stty(fd,argp)    ioctl(fd,TIOCSETN,argp)
  55.  
  56. #define TBUFSIZ    1024
  57. #define LINSIZ    256
  58. #define ctrl(letter)    (letter & 077)
  59. #define RUBOUT    '\177'
  60. #define ESC    '\033'
  61. #define QUIT    '\034'
  62.  
  63. struct sgttyb    otty, savetty;
  64. long        file_pos, file_size;
  65. int        fnum, no_intty, no_tty, slow_tty;
  66. int        dum_opt, dlines, onquit(), end_it(), chgwinsz();
  67. int        onsusp();
  68. int        nscroll = 11;    /* Number of lines scrolled by 'd' */
  69. int        fold_opt = 1;    /* Fold long lines */
  70. int        stop_opt = 1;    /* Stop after form feeds */
  71. int        ssp_opt = 0;    /* Suppress white space */
  72. int        ul_opt = 1;    /* Underline as best we can */
  73. int        promptlen;
  74. int        Currline;    /* Line we are currently at */
  75. int        startup = 1;
  76. int        firstf = 1;
  77. int        notell = 1;
  78. int        docrterase = 0;
  79. int        docrtkill = 0;
  80. int        bad_so;    /* True if overwriting does not turn off standout */
  81. int        inwait, Pause, errors;
  82. int        within;    /* true if we are within a file,
  83.             false if we are between files */
  84. int        hard, dumb, noscroll, hardtabs, clreol, eatnl;
  85. int        catch_susp;    /* We should catch the SIGTSTP signal */
  86. char        **fnames;    /* The list of file names */
  87. int        nfiles;        /* Number of files left to process */
  88. char        *shell;        /* The name of the shell to use */
  89. int        shellp;        /* A previous shell command exists */
  90. char        ch;
  91. jmp_buf        restore;
  92. char        Line[LINSIZ];    /* Line buffer */
  93. int        Lpp = 24;    /* lines per page */
  94. char        *Clear;        /* clear screen */
  95. char        *eraseln;    /* erase line */
  96. char        *Senter, *Sexit;/* enter and exit standout mode */
  97. char        *ULenter, *ULexit;    /* enter and exit underline mode */
  98. char        *chUL;        /* underline character */
  99. char        *chBS;        /* backspace character */
  100. char        *Home;        /* go to home */
  101. char        *cursorm;    /* cursor movement */
  102. char        cursorhome[40];    /* contains cursor movement to home */
  103. char        *EodClr;    /* clear rest of screen */
  104. char        *tgetstr();
  105. int        Mcol = 80;    /* number of columns */
  106. int        Wrap = 1;    /* set if automargins */
  107. int        soglitch;    /* terminal has standout mode glitch */
  108. int        ulglitch;    /* terminal has underline mode glitch */
  109. int        pstate = 0;    /* current UL state */
  110. long        fseek();
  111. char        *getenv();
  112. struct {
  113.     long chrctr, line;
  114. } context, screen_start;
  115. extern char    PC;        /* pad character */
  116. extern short    ospeed;
  117.  
  118. /*
  119.  * Forward declarations, to keep gcc happy:
  120.  */
  121.  
  122. extern int magic();
  123.  
  124. main(argc, argv)
  125. int argc;
  126. char *argv[];
  127. {
  128.     register FILE    *f;
  129.     register char    *s;
  130.     register char    *p;
  131.     register char    ch;
  132.     register int    left;
  133.     int            prnames = 0;
  134.     int            initopt = 0;
  135.     int            srchopt = 0;
  136.     int            clearit = 0;
  137.     int            initline;
  138.     char        initbuf[80];
  139.     FILE        *checkf();
  140.  
  141.     nfiles = argc;
  142.     fnames = argv;
  143.     initterm ();
  144.     nscroll = Lpp/2 - 1;
  145.     if (nscroll <= 0)
  146.     nscroll = 1;
  147.     if(s = getenv("MORE")) argscan(s);
  148.     while (--nfiles > 0) {
  149.     if ((ch = (*++fnames)[0]) == '-') {
  150.         argscan(*fnames+1);
  151.     }
  152.     else if (ch == '+') {
  153.         s = *fnames;
  154.         if (*++s == '/') {
  155.         srchopt++;
  156.         for (++s, p = initbuf; p < initbuf + 79 && *s != '\0';)
  157.             *p++ = *s++;
  158.         *p = '\0';
  159.         }
  160.         else {
  161.         initopt++;
  162.         for (initline = 0; *s != '\0'; s++)
  163.             if (isdigit (*s))
  164.             initline = initline*10 + *s -'0';
  165.         --initline;
  166.         }
  167.     }
  168.     else break;
  169.     }
  170.     /* allow clreol only if Home and eraseln and EodClr strings are
  171.      *  defined, and in that case, make sure we are in noscroll mode
  172.      */
  173.     if(clreol)
  174.     {
  175.         if((Home == NULL) || (*Home == '\0') ||
  176.        (eraseln == NULL) || (*eraseln == '\0') ||
  177.            (EodClr == NULL) || (*EodClr == '\0') )
  178.           clreol = 0;
  179.     else noscroll = 1;
  180.     }
  181.     if (dlines == 0)
  182.     dlines = Lpp - (noscroll ? 1 : 2);
  183.     left = dlines;
  184.     if (nfiles > 1)
  185.     prnames++;
  186.     if (!no_intty && nfiles == 0) {
  187.     char *rindex();
  188.  
  189.     p = rindex(argv[0], '/');
  190.     fputs("usage: ",stderr);
  191.     fputs(p ? p + 1 : argv[0],stderr);
  192.     fputs(" [-dfln] [+linenum | +/pattern] name1 name2 ...\n",stderr);
  193.     exit(1);
  194.     }
  195.     else
  196.     f = stdin;
  197.     if (!no_tty) {
  198.     signal(SIGQUIT, onquit);
  199.     signal(SIGINT, end_it);
  200.     signal(SIGWINCH, chgwinsz);
  201.     if (signal (SIGTSTP, SIG_IGN) == SIG_DFL) {
  202.         signal(SIGTSTP, onsusp);
  203.         catch_susp++;
  204.     }
  205.     stty (fileno(stderr), &otty);
  206.     }
  207.     if (no_intty) {
  208.     if (no_tty)
  209.         copy_file (stdin);
  210.     else {
  211.         if ((ch = Getc (f)) == '\f')
  212.         doclear();
  213.         else {
  214.         Ungetc (ch, f);
  215.         if (noscroll && (ch != EOF)) {
  216.             if (clreol)
  217.             home ();
  218.             else
  219.             doclear ();
  220.         }
  221.         }
  222.         if (srchopt)
  223.         {
  224.         search (initbuf, stdin, 1);
  225.         if (noscroll)
  226.             left--;
  227.         }
  228.         else if (initopt)
  229.         skiplns (initline, stdin);
  230.         screen (stdin, left);
  231.     }
  232.     no_intty = 0;
  233.     prnames++;
  234.     firstf = 0;
  235.     }
  236.  
  237.     while (fnum < nfiles) {
  238.     if ((f = checkf (fnames[fnum], &clearit)) != NULL) {
  239.         context.line = context.chrctr = 0;
  240.         Currline = 0;
  241.         if (firstf) setjmp (restore);
  242.         if (firstf) {
  243.         firstf = 0;
  244.         if (srchopt)
  245.         {
  246.             search (initbuf, f, 1);
  247.             if (noscroll)
  248.             left--;
  249.         }
  250.         else if (initopt)
  251.             skiplns (initline, f);
  252.         }
  253.         else if (fnum < nfiles && !no_tty) {
  254.         setjmp (restore);
  255.         left = command (fnames[fnum], f);
  256.         }
  257.         if (left != 0) {
  258.         if ((noscroll || clearit) && (file_size != LONG_MAX))
  259.             if (clreol)
  260.             home ();
  261.             else
  262.             doclear ();
  263.         if (prnames) {
  264.             if (bad_so)
  265.             erase (0);
  266.             if (clreol)
  267.             cleareol ();
  268.             pr("::::::::::::::");
  269.             if (promptlen > 14)
  270.             erase (14);
  271.             printf ("\n");
  272.             if(clreol) cleareol();
  273.             printf("%s\n", fnames[fnum]);
  274.             if(clreol) cleareol();
  275.             printf("::::::::::::::\n");
  276.             if (left > Lpp - 4)
  277.             left = Lpp - 4;
  278.         }
  279.         if (no_tty)
  280.             copy_file (f);
  281.         else {
  282.             within++;
  283.             screen(f, left);
  284.             within = 0;
  285.         }
  286.         }
  287.         setjmp (restore);
  288.         fflush(stdout);
  289.         fclose(f);
  290.         screen_start.line = screen_start.chrctr = 0L;
  291.         context.line = context.chrctr = 0L;
  292.     }
  293.     fnum++;
  294.     firstf = 0;
  295.     }
  296.     reset_tty ();
  297.     exit(0);
  298. }
  299.  
  300. argscan(s)
  301. char *s;
  302. {
  303.     int seen_num = 0;
  304.  
  305.     while (*s != '\0') {
  306.         switch (*s) {
  307.           case '0': case '1': case '2':
  308.           case '3': case '4': case '5':
  309.           case '6': case '7': case '8':
  310.           case '9':
  311.             if (!seen_num) {
  312.                 dlines = 0;
  313.                 seen_num = 1;
  314.             }
  315.             dlines = dlines*10 + *s - '0';
  316.             break;
  317.           case 'd':
  318.             dum_opt = 1;
  319.             break;
  320.           case 'l':
  321.             stop_opt = 0;
  322.             break;
  323.           case 'f':
  324.             fold_opt = 0;
  325.             break;
  326.           case 'p':
  327.             noscroll++;
  328.             break;
  329.           case 'c':
  330.             clreol++;
  331.             break;
  332.           case 's':
  333.             ssp_opt = 1;
  334.             break;
  335.           case 'u':
  336.             ul_opt = 0;
  337.             break;
  338.         }
  339.         s++;
  340.     }
  341. }
  342.  
  343.  
  344. /*
  345. ** Check whether the file named by fs is an ASCII file which the user may
  346. ** access.  If it is, return the opened file. Otherwise return NULL.
  347. */
  348.  
  349. FILE *
  350. checkf (fs, clearfirst)
  351.     register char *fs;
  352.     int *clearfirst;
  353. {
  354.     struct stat stbuf;
  355.     register FILE *f;
  356.     char c;
  357.  
  358.     if (stat (fs, &stbuf) == -1) {
  359.         (void)fflush(stdout);
  360.         if (clreol)
  361.             cleareol ();
  362.         perror(fs);
  363.         return((FILE *)NULL);
  364.     }
  365.     if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
  366.         printf("\n*** %s: directory ***\n\n", fs);
  367.         return((FILE *)NULL);
  368.     }
  369.     if ((f = Fopen(fs, "r")) == NULL) {
  370.         (void)fflush(stdout);
  371.         perror(fs);
  372.         return((FILE *)NULL);
  373.     }
  374.     if (magic(f, fs))
  375.         return((FILE *)NULL);
  376.     c = Getc(f);
  377.     *clearfirst = c == '\f';
  378.     Ungetc (c, f);
  379.     if ((file_size = stbuf.st_size) == 0)
  380.         file_size = LONG_MAX;
  381.     return(f);
  382. }
  383.  
  384. /*
  385.  * magic --
  386.  *    check for file magic numbers.  This code would best be shared with
  387.  *    the file(1) program or, perhaps, more should not try and be so smart?
  388.  */
  389. static
  390. magic(f, fs)
  391.     FILE *f;
  392.     char *fs;
  393. {
  394.     struct exec ex;
  395.  
  396.     if (fread(&ex, sizeof(ex), 1, f) == 1)
  397.         switch(ex.a_magic) {
  398.         case OMAGIC:
  399.         case NMAGIC:
  400.         case ZMAGIC:
  401.         case 0405:
  402.         case 0411:
  403.         case 0177545:
  404.             printf("\n******** %s: Not a text file ********\n\n", fs);
  405.             (void)fclose(f);
  406.             return(1);
  407.         }
  408.     (void)fseek(f, 0L, L_SET);        /* rewind() not necessary */
  409.     return(0);
  410. }
  411.  
  412. /*
  413. ** A real function, for the tputs routine in termlib
  414. */
  415.  
  416. putch (ch)
  417. char ch;
  418. {
  419.     putchar (ch);
  420. }
  421.  
  422. /*
  423. ** Print out the contents of the file f, one screenful at a time.
  424. */
  425.  
  426. #define STOP -10
  427.  
  428. screen (f, num_lines)
  429. register FILE *f;
  430. register int num_lines;
  431. {
  432.     register int c;
  433.     register int nchars;
  434.     int length;            /* length of current line */
  435.     static int prev_len = 1;    /* length of previous line */
  436.  
  437.     for (;;) {
  438.     while (num_lines > 0 && !Pause) {
  439.         if ((nchars = getline (f, &length)) == EOF)
  440.         {
  441.         if (clreol)
  442.             clreos();
  443.         return;
  444.         }
  445.         if (ssp_opt && length == 0 && prev_len == 0)
  446.         continue;
  447.         prev_len = length;
  448.         if (bad_so || (Senter && *Senter == ' ') && promptlen > 0)
  449.         erase (0);
  450.         /* must clear before drawing line since tabs on some terminals
  451.          * do not erase what they tab over.
  452.          */
  453.         if (clreol)
  454.         cleareol ();
  455.         prbuf (Line, length);
  456.         if (nchars < promptlen)
  457.         erase (nchars);    /* erase () sets promptlen to 0 */
  458.         else promptlen = 0;
  459.         /* is this needed?
  460.          * if (clreol)
  461.          *    cleareol();    /* must clear again in case we wrapped *
  462.          */
  463.         if (nchars < Mcol || !fold_opt)
  464.         prbuf("\n", 1);    /* will turn off UL if necessary */
  465.         if (nchars == STOP)
  466.         break;
  467.         num_lines--;
  468.     }
  469.     if (pstate) {
  470.         tputs(ULexit, 1, putch);
  471.         pstate = 0;
  472.     }
  473.     fflush(stdout);
  474.     if ((c = Getc(f)) == EOF)
  475.     {
  476.         if (clreol)
  477.         clreos ();
  478.         return;
  479.     }
  480.  
  481.     if (Pause && clreol)
  482.         clreos ();
  483.     Ungetc (c, f);
  484.     setjmp (restore);
  485.     Pause = 0; startup = 0;
  486.     if ((num_lines = command (NULL, f)) == 0)
  487.         return;
  488.     if (hard && promptlen > 0)
  489.         erase (0);
  490.     if (noscroll && num_lines >= dlines)
  491.     {
  492.         if (clreol)
  493.         home();
  494.         else
  495.         doclear ();
  496.     }
  497.     screen_start.line = Currline;
  498.     screen_start.chrctr = Ftell (f);
  499.     }
  500. }
  501.  
  502. /*
  503. ** Come here if a quit signal is received
  504. */
  505.  
  506. onquit()
  507. {
  508.     signal(SIGQUIT, SIG_IGN);
  509.     if (!inwait) {
  510.     putchar ('\n');
  511.     if (!startup) {
  512.         signal(SIGQUIT, onquit);
  513.         longjmp (restore, 1);
  514.     }
  515.     else
  516.         Pause++;
  517.     }
  518.     else if (!dum_opt && notell) {
  519.     write (2, "[Use q or Q to quit]", 20);
  520.     promptlen += 20;
  521.     notell = 0;
  522.     }
  523.     signal(SIGQUIT, onquit);
  524. }
  525.  
  526. /*
  527. ** Come here if a signal for a window size change is received
  528. */
  529.  
  530. chgwinsz()
  531. {
  532.     struct winsize win;
  533.  
  534.     (void) signal(SIGWINCH, SIG_IGN);
  535.     if (ioctl(fileno(stdout), TIOCGWINSZ, &win) != -1) {
  536.     if (win.ws_row != 0) {
  537.         Lpp = win.ws_row;
  538.         nscroll = Lpp/2 - 1;
  539.         if (nscroll <= 0)
  540.         nscroll = 1;
  541.         dlines = Lpp - (noscroll ? 1 : 2);
  542.     }
  543.     if (win.ws_col != 0)
  544.         Mcol = win.ws_col;
  545.     }
  546.     (void) signal(SIGWINCH, chgwinsz);
  547. }
  548.  
  549. /*
  550. ** Clean up terminal state and exit. Also come here if interrupt signal received
  551. */
  552.  
  553. end_it ()
  554. {
  555.  
  556.     reset_tty ();
  557.     if (clreol) {
  558.     putchar ('\r');
  559.     clreos ();
  560.     fflush (stdout);
  561.     }
  562.     else if (!clreol && (promptlen > 0)) {
  563.     kill_line ();
  564.     fflush (stdout);
  565.     }
  566.     else
  567.     write (2, "\n", 1);
  568.     _exit(0);
  569. }
  570.  
  571. copy_file(f)
  572. register FILE *f;
  573. {
  574.     register int c;
  575.  
  576.     while ((c = getc(f)) != EOF)
  577.     putchar(c);
  578. }
  579.  
  580. /* Simplified printf function */
  581.  
  582. printf (va_alist)
  583. va_dcl
  584. {
  585.     va_list ap;
  586.     register char ch;
  587.     register int ccount;
  588.     register char *fmt;
  589.  
  590.     ccount = 0;
  591.     va_start(ap);
  592.     fmt = va_arg(ap, char *);
  593.     while (*fmt) {
  594.         while ((ch = *fmt++) != '%') {
  595.             if (ch == '\0')
  596.                 return (ccount);
  597.             ccount++;
  598.             putchar (ch);
  599.         }
  600.         switch (*fmt++) {
  601.         case 'd':
  602.             ccount += printd (va_arg(ap, int));
  603.             break;
  604.         case 's':
  605.             ccount += pr (va_arg(ap, char *));
  606.             break;
  607.         case '%':
  608.             ccount++;
  609.             putchar ('%');
  610.             break;
  611.         case '0':
  612.             return (ccount);
  613.         default:
  614.             break;
  615.         }
  616.     }
  617.     va_end(ap);
  618.     return (ccount);
  619.  
  620. }
  621.  
  622. /*
  623. ** Print an integer as a string of decimal digits,
  624. ** returning the length of the print representation.
  625. */
  626.  
  627. printd (n)
  628. int n;
  629. {
  630.     int a, nchars;
  631.  
  632.     if (a = n/10)
  633.     nchars = 1 + printd(a);
  634.     else
  635.     nchars = 1;
  636.     putchar (n % 10 + '0');
  637.     return (nchars);
  638. }
  639.  
  640. /* Put the print representation of an integer into a string */
  641. static char *sptr;
  642.  
  643. scanstr (n, str)
  644. int n;
  645. char *str;
  646. {
  647.     sptr = str;
  648.     Sprintf (n);
  649.     *sptr = '\0';
  650. }
  651.  
  652. Sprintf (n)
  653. {
  654.     int a;
  655.  
  656.     if (a = n/10)
  657.     Sprintf (a);
  658.     *sptr++ = n % 10 + '0';
  659. }
  660.  
  661. static char bell = ctrl('G');
  662.  
  663. strlen (s)
  664. char *s;
  665. {
  666.     register char *p;
  667.  
  668.     p = s;
  669.     while (*p++)
  670.     ;
  671.     return (p - s - 1);
  672. }
  673.  
  674. /* See whether the last component of the path name "path" is equal to the
  675. ** string "string"
  676. */
  677.  
  678. tailequ (path, string)
  679. char *path;
  680. register char *string;
  681. {
  682.     register char *tail;
  683.  
  684.     tail = path + strlen(path);
  685.     while (tail >= path)
  686.         if (*(--tail) == '/')
  687.             break;
  688.     ++tail;
  689.     while (*tail++ == *string++)
  690.         if (*tail == '\0')
  691.             return(1);
  692.     return(0);
  693. }
  694.  
  695. prompt (filename)
  696. char *filename;
  697. {
  698.     if (clreol)
  699.     cleareol ();
  700.     else if (promptlen > 0)
  701.     kill_line ();
  702.     if (!hard) {
  703.     promptlen = 8;
  704.     if (Senter && Sexit) {
  705.         tputs (Senter, 1, putch);
  706.         promptlen += (2 * soglitch);
  707.     }
  708.     if (clreol)
  709.         cleareol ();
  710.     pr("--More--");
  711.     if (filename != NULL) {
  712.         promptlen += printf ("(Next file: %s)", filename);
  713.     }
  714.     else if (!no_intty) {
  715.         promptlen += printf ("(%d%%)", (int)((file_pos * 100) / file_size));
  716.     }
  717.     if (dum_opt) {
  718.         promptlen += pr("[Press space to continue, 'q' to quit.]");
  719.     }
  720.     if (Senter && Sexit)
  721.         tputs (Sexit, 1, putch);
  722.     if (clreol)
  723.         clreos ();
  724.     fflush(stdout);
  725.     }
  726.     else
  727.     write (2, &bell, 1);
  728.     inwait++;
  729. }
  730.  
  731. /*
  732. ** Get a logical line
  733. */
  734.  
  735. getline(f, length)
  736. register FILE *f;
  737. int *length;
  738. {
  739.     register int    c;
  740.     register char    *p;
  741.     register int    column;
  742.     static int        colflg;
  743.  
  744.     p = Line;
  745.     column = 0;
  746.     c = Getc (f);
  747.     if (colflg && c == '\n') {
  748.     Currline++;
  749.     c = Getc (f);
  750.     }
  751.     while (p < &Line[LINSIZ - 1]) {
  752.     if (c == EOF) {
  753.         if (p > Line) {
  754.         *p = '\0';
  755.         *length = p - Line;
  756.         return (column);
  757.         }
  758.         *length = p - Line;
  759.         return (EOF);
  760.     }
  761.     if (c == '\n') {
  762.         Currline++;
  763.         break;
  764.     }
  765.     *p++ = c;
  766.     if (c == '\t')
  767.         if (!hardtabs || column < promptlen && !hard) {
  768.         if (hardtabs && eraseln && !dumb) {
  769.             column = 1 + (column | 7);
  770.             tputs (eraseln, 1, putch);
  771.             promptlen = 0;
  772.         }
  773.         else {
  774.             for (--p; p < &Line[LINSIZ - 1];) {
  775.             *p++ = ' ';
  776.             if ((++column & 7) == 0)
  777.                 break;
  778.             }
  779.             if (column >= promptlen) promptlen = 0;
  780.         }
  781.         }
  782.         else
  783.         column = 1 + (column | 7);
  784.     else if (c == '\b' && column > 0)
  785.         column--;
  786.     else if (c == '\r')
  787.         column = 0;
  788.     else if (c == '\f' && stop_opt) {
  789.         p[-1] = '^';
  790.         *p++ = 'L';
  791.         column += 2;
  792.         Pause++;
  793.     }
  794.     else if (c == EOF) {
  795.         *length = p - Line;
  796.         return (column);
  797.     }
  798.     else if (c >= ' ' && c != RUBOUT)
  799.         column++;
  800.     if (column >= Mcol && fold_opt) break;
  801.     c = Getc (f);
  802.     }
  803.     if (column >= Mcol && Mcol > 0) {
  804.     if (!Wrap) {
  805.         *p++ = '\n';
  806.     }
  807.     }
  808.     colflg = column == Mcol && fold_opt;
  809.     if (colflg && eatnl && Wrap) {
  810.     *p++ = '\n'; /* simulate normal wrap */
  811.     }
  812.     *length = p - Line;
  813.     *p = 0;
  814.     return (column);
  815. }
  816.  
  817. /*
  818. ** Erase the rest of the prompt, assuming we are starting at column col.
  819. */
  820.  
  821. erase (col)
  822. register int col;
  823. {
  824.  
  825.     if (promptlen == 0)
  826.     return;
  827.     if (hard) {
  828.     putchar ('\n');
  829.     }
  830.     else {
  831.     if (col == 0)
  832.         putchar ('\r');
  833.     if (!dumb && eraseln)
  834.         tputs (eraseln, 1, putch);
  835.     else
  836.         for (col = promptlen - col; col > 0; col--)
  837.         putchar (' ');
  838.     }
  839.     promptlen = 0;
  840. }
  841.  
  842. /*
  843. ** Erase the current line entirely
  844. */
  845.  
  846. kill_line ()
  847. {
  848.     erase (0);
  849.     if (!eraseln || dumb) putchar ('\r');
  850. }
  851.  
  852. /*
  853.  * force clear to end of line
  854.  */
  855. cleareol()
  856. {
  857.     tputs(eraseln, 1, putch);
  858. }
  859.  
  860. clreos()
  861. {
  862.     tputs(EodClr, 1, putch);
  863. }
  864.  
  865. /*
  866. **  Print string and return number of characters
  867. */
  868.  
  869. pr(s1)
  870. char    *s1;
  871. {
  872.     register char    *s;
  873.     register char    c;
  874.  
  875.     for (s = s1; c = *s++; )
  876.     putchar(c);
  877.     return (s - s1 - 1);
  878. }
  879.  
  880.  
  881. /* Print a buffer of n characters */
  882.  
  883. prbuf (s, n)
  884. register char *s;
  885. register int n;
  886. {
  887.     register char c;            /* next output character */
  888.     register int state;            /* next output char's UL state */
  889. #define wouldul(s,n)    ((n) >= 2 && (((s)[0] == '_' && (s)[1] == '\b') || ((s)[1] == '\b' && (s)[2] == '_')))
  890.  
  891.     while (--n >= 0)
  892.     if (!ul_opt)
  893.         putchar (*s++);
  894.     else {
  895.         if (*s == ' ' && pstate == 0 && ulglitch && wouldul(s+1, n-1)) {
  896.         s++;
  897.         continue;
  898.         }
  899.         if (state = wouldul(s, n)) {
  900.         c = (*s == '_')? s[2] : *s ;
  901.         n -= 2;
  902.         s += 3;
  903.         } else
  904.         c = *s++;
  905.         if (state != pstate) {
  906.         if (c == ' ' && state == 0 && ulglitch && wouldul(s, n-1))
  907.             state = 1;
  908.         else
  909.             tputs(state ? ULenter : ULexit, 1, putch);
  910.         }
  911.         if (c != ' ' || pstate == 0 || state != 0 || ulglitch == 0)
  912.             putchar(c);
  913.         if (state && *chUL) {
  914.         pr(chBS);
  915.         tputs(chUL, 1, putch);
  916.         }
  917.         pstate = state;
  918.     }
  919. }
  920.  
  921. /*
  922. **  Clear the screen
  923. */
  924.  
  925. doclear()
  926. {
  927.     if (Clear && !hard) {
  928.     tputs(Clear, 1, putch);
  929.  
  930.     /* Put out carriage return so that system doesn't
  931.     ** get confused by escape sequences when expanding tabs
  932.     */
  933.     putchar ('\r');
  934.     promptlen = 0;
  935.     }
  936. }
  937.  
  938. /*
  939.  * Go to home position
  940.  */
  941. home()
  942. {
  943.     tputs(Home,1,putch);
  944. }
  945.  
  946. static int lastcmd, lastarg, lastp;
  947. static int lastcolon;
  948. char shell_line[132];
  949.  
  950. /*
  951. ** Read a command and do it. A command consists of an optional integer
  952. ** argument followed by the command character.  Return the number of lines
  953. ** to display in the next screenful.  If there is nothing more to display
  954. ** in the current file, zero is returned.
  955. */
  956.  
  957. command (filename, f)
  958. char *filename;
  959. register FILE *f;
  960. {
  961.     register int nlines;
  962.     register int retval;
  963.     register char c;
  964.     char colonch;
  965.     FILE *helpf;
  966.     int done;
  967.     char comchar, cmdbuf[80], *p;
  968.  
  969. #define ret(val) retval=val;done++;break
  970.  
  971.     done = 0;
  972.     if (!errors)
  973.     prompt (filename);
  974.     else
  975.     errors = 0;
  976.     if (MBIT == RAW && slow_tty) {
  977.     otty.sg_flags |= MBIT;
  978.     stty(fileno(stderr), &otty);
  979.     }
  980.     for (;;) {
  981.     nlines = number (&comchar);
  982.     lastp = colonch = 0;
  983.     if (comchar == '.') {    /* Repeat last command */
  984.         lastp++;
  985.         comchar = lastcmd;
  986.         nlines = lastarg;
  987.         if (lastcmd == ':')
  988.             colonch = lastcolon;
  989.     }
  990.     lastcmd = comchar;
  991.     lastarg = nlines;
  992.     if (comchar == otty.sg_erase) {
  993.         kill_line ();
  994.         prompt (filename);
  995.         continue;
  996.     }
  997.     switch (comchar) {
  998.     case ':':
  999.         retval = colon (filename, colonch, nlines);
  1000.         if (retval >= 0)
  1001.         done++;
  1002.         break;
  1003.     case 'b':
  1004.     case ctrl('B'):
  1005.         {
  1006.         register int initline;
  1007.  
  1008.         if (no_intty) {
  1009.             write(2, &bell, 1);
  1010.             return (-1);
  1011.         }
  1012.  
  1013.         if (nlines == 0) nlines++;
  1014.  
  1015.         putchar ('\r');
  1016.         erase (0);
  1017.         printf ("\n");
  1018.         if (clreol)
  1019.             cleareol ();
  1020.         printf ("...back %d page", nlines);
  1021.         if (nlines > 1)
  1022.             pr ("s\n");
  1023.         else
  1024.             pr ("\n");
  1025.  
  1026.         if (clreol)
  1027.             cleareol ();
  1028.         pr ("\n");
  1029.  
  1030.         initline = Currline - dlines * (nlines + 1);
  1031.         if (! noscroll)
  1032.             --initline;
  1033.         if (initline < 0) initline = 0;
  1034.         Fseek(f, 0L);
  1035.         Currline = 0;    /* skiplns() will make Currline correct */
  1036.         skiplns(initline, f);
  1037.         if (! noscroll) {
  1038.             ret(dlines + 1);
  1039.         }
  1040.         else {
  1041.             ret(dlines);
  1042.         }
  1043.         }
  1044.     case ' ':
  1045.     case 'z':
  1046.         if (nlines == 0) nlines = dlines;
  1047.         else if (comchar == 'z') dlines = nlines;
  1048.         ret (nlines);
  1049.     case 'd':
  1050.     case ctrl('D'):
  1051.         if (nlines != 0) nscroll = nlines;
  1052.         ret (nscroll);
  1053.     case 'q':
  1054.     case 'Q':
  1055.         end_it ();
  1056.     case 's':
  1057.     case 'f':
  1058.         if (nlines == 0) nlines++;
  1059.         if (comchar == 'f')
  1060.         nlines *= dlines;
  1061.         putchar ('\r');
  1062.         erase (0);
  1063.         printf ("\n");
  1064.         if (clreol)
  1065.         cleareol ();
  1066.         printf ("...skipping %d line", nlines);
  1067.         if (nlines > 1)
  1068.         pr ("s\n");
  1069.         else
  1070.         pr ("\n");
  1071.  
  1072.         if (clreol)
  1073.         cleareol ();
  1074.         pr ("\n");
  1075.  
  1076.         while (nlines > 0) {
  1077.         while ((c = Getc (f)) != '\n')
  1078.             if (c == EOF) {
  1079.             retval = 0;
  1080.             done++;
  1081.             goto endsw;
  1082.             }
  1083.             Currline++;
  1084.             nlines--;
  1085.         }
  1086.         ret (dlines);
  1087.     case '\n':
  1088.         if (nlines != 0)
  1089.         dlines = nlines;
  1090.         else
  1091.         nlines = 1;
  1092.         ret (nlines);
  1093.     case '\f':
  1094.         if (!no_intty) {
  1095.         doclear ();
  1096.         Fseek (f, screen_start.chrctr);
  1097.         Currline = screen_start.line;
  1098.         ret (dlines);
  1099.         }
  1100.         else {
  1101.         write (2, &bell, 1);
  1102.         break;
  1103.         }
  1104.     case '\'':
  1105.         if (!no_intty) {
  1106.         kill_line ();
  1107.         pr ("\n***Back***\n\n");
  1108.         Fseek (f, context.chrctr);
  1109.         Currline = context.line;
  1110.         ret (dlines);
  1111.         }
  1112.         else {
  1113.         write (2, &bell, 1);
  1114.         break;
  1115.         }
  1116.     case '=':
  1117.         kill_line ();
  1118.         promptlen = printd (Currline);
  1119.         fflush (stdout);
  1120.         break;
  1121.     case 'n':
  1122.         lastp++;
  1123.     case '/':
  1124.         if (nlines == 0) nlines++;
  1125.         kill_line ();
  1126.         pr ("/");
  1127.         promptlen = 1;
  1128.         fflush (stdout);
  1129.         if (lastp) {
  1130.         write (2,"\r", 1);
  1131.         search (NULL, f, nlines);    /* Use previous r.e. */
  1132.         }
  1133.         else {
  1134.         ttyin (cmdbuf, 78, '/');
  1135.         write (2, "\r", 1);
  1136.         search (cmdbuf, f, nlines);
  1137.         }
  1138.         ret (dlines-1);
  1139.     case '!':
  1140.         do_shell (filename);
  1141.         break;
  1142.     case '?':
  1143.     case 'h':
  1144.         if ((helpf = fopen (HELPFILE, "r")) == NULL)
  1145.         error ("Can't open help file");
  1146.         if (noscroll) doclear ();
  1147.         copy_file (helpf);
  1148.         fclose (helpf);
  1149.         prompt (filename);
  1150.         break;
  1151.     case 'v':    /* This case should go right before default */
  1152.         if (!no_intty) {
  1153.         kill_line ();
  1154.         cmdbuf[0] = '+';
  1155.         scanstr (Currline - dlines < 0 ? 0
  1156.                 : Currline - (dlines + 1) / 2, &cmdbuf[1]);
  1157.         pr ("vi "); pr (cmdbuf); putchar (' '); pr (fnames[fnum]);
  1158.         execute (filename, VI, "vi", cmdbuf, fnames[fnum], 0);
  1159.         break;
  1160.         }
  1161.     default:
  1162.         if (dum_opt) {
  1163.            kill_line ();
  1164.         if (Senter && Sexit) {
  1165.             tputs (Senter, 1, putch);
  1166.             promptlen = pr ("[Press 'h' for instructions.]") + (2 * soglitch);
  1167.             tputs (Sexit, 1, putch);
  1168.         }
  1169.         else
  1170.             promptlen = pr ("[Press 'h' for instructions.]");
  1171.         fflush (stdout);
  1172.         }
  1173.         else
  1174.         write (2, &bell, 1);
  1175.         break;
  1176.     }
  1177.     if (done) break;
  1178.     }
  1179.     putchar ('\r');
  1180. endsw:
  1181.     inwait = 0;
  1182.     notell++;
  1183.     if (MBIT == RAW && slow_tty) {
  1184.     otty.sg_flags &= ~MBIT;
  1185.     stty(fileno(stderr), &otty);
  1186.     }
  1187.     return (retval);
  1188. }
  1189.  
  1190. char ch;
  1191.  
  1192. /*
  1193.  * Execute a colon-prefixed command.
  1194.  * Returns <0 if not a command that should cause
  1195.  * more of the file to be printed.
  1196.  */
  1197.  
  1198. colon (filename, cmd, nlines)
  1199. char *filename;
  1200. int cmd;
  1201. int nlines;
  1202. {
  1203.     if (cmd == 0)
  1204.         ch = readch ();
  1205.     else
  1206.         ch = cmd;
  1207.     lastcolon = ch;
  1208.     switch (ch) {
  1209.     case 'f':
  1210.         kill_line ();
  1211.         if (!no_intty)
  1212.             promptlen = printf ("\"%s\" line %d", fnames[fnum], Currline);
  1213.         else
  1214.             promptlen = printf ("[Not a file] line %d", Currline);
  1215.         fflush (stdout);
  1216.         return (-1);
  1217.     case 'n':
  1218.         if (nlines == 0) {
  1219.             if (fnum >= nfiles - 1)
  1220.                 end_it ();
  1221.             nlines++;
  1222.         }
  1223.         putchar ('\r');
  1224.         erase (0);
  1225.         skipf (nlines);
  1226.         return (0);
  1227.     case 'p':
  1228.         if (no_intty) {
  1229.             write (2, &bell, 1);
  1230.             return (-1);
  1231.         }
  1232.         putchar ('\r');
  1233.         erase (0);
  1234.         if (nlines == 0)
  1235.             nlines++;
  1236.         skipf (-nlines);
  1237.         return (0);
  1238.     case '!':
  1239.         do_shell (filename);
  1240.         return (-1);
  1241.     case 'q':
  1242.     case 'Q':
  1243.         end_it ();
  1244.     default:
  1245.         write (2, &bell, 1);
  1246.         return (-1);
  1247.     }
  1248. }
  1249.  
  1250. /*
  1251. ** Read a decimal number from the terminal. Set cmd to the non-digit which
  1252. ** terminates the number.
  1253. */
  1254.  
  1255. number(cmd)
  1256. char *cmd;
  1257. {
  1258.     register int i;
  1259.  
  1260.     i = 0; ch = otty.sg_kill;
  1261.     for (;;) {
  1262.         ch = readch ();
  1263.         if (ch >= '0' && ch <= '9')
  1264.             i = i*10 + ch - '0';
  1265.         else if (ch == otty.sg_kill)
  1266.             i = 0;
  1267.         else {
  1268.             *cmd = ch;
  1269.             break;
  1270.         }
  1271.     }
  1272.     return (i);
  1273. }
  1274.  
  1275. do_shell (filename)
  1276. char *filename;
  1277. {
  1278.     char cmdbuf[80];
  1279.  
  1280.     kill_line ();
  1281.     pr ("!");
  1282.     fflush (stdout);
  1283.     promptlen = 1;
  1284.     if (lastp)
  1285.         pr (shell_line);
  1286.     else {
  1287.         ttyin (cmdbuf, 78, '!');
  1288.         if (expand (shell_line, cmdbuf)) {
  1289.             kill_line ();
  1290.             promptlen = printf ("!%s", shell_line);
  1291.         }
  1292.     }
  1293.     fflush (stdout);
  1294.     write (2, "\n", 1);
  1295.     promptlen = 0;
  1296.     shellp = 1;
  1297.     execute (filename, shell, shell, "-c", shell_line, 0);
  1298. }
  1299.  
  1300. /*
  1301. ** Search for nth ocurrence of regular expression contained in buf in the file
  1302. */
  1303.  
  1304. search (buf, file, n)
  1305. char buf[];
  1306. FILE *file;
  1307. register int n;
  1308. {
  1309.     long startline = Ftell (file);
  1310.     register long line1 = startline;
  1311.     register long line2 = startline;
  1312.     register long line3 = startline;
  1313.     register int lncount;
  1314.     int saveln, rv, re_exec();
  1315.     char *s, *re_comp();
  1316.  
  1317.     context.line = saveln = Currline;
  1318.     context.chrctr = startline;
  1319.     lncount = 0;
  1320.     if ((s = re_comp (buf)) != 0)
  1321.     error (s);
  1322.     while (!feof (file)) {
  1323.     line3 = line2;
  1324.     line2 = line1;
  1325.     line1 = Ftell (file);
  1326.     rdline (file);
  1327.     lncount++;
  1328.     if ((rv = re_exec (Line)) == 1)
  1329.         if (--n == 0) {
  1330.             if (lncount > 3 || (lncount > 1 && no_intty))
  1331.             {
  1332.             pr ("\n");
  1333.             if (clreol)
  1334.                 cleareol ();
  1335.             pr("...skipping\n");
  1336.             }
  1337.             if (!no_intty) {
  1338.             Currline -= (lncount >= 3 ? 3 : lncount);
  1339.             Fseek (file, line3);
  1340.             if (noscroll)
  1341.                 if (clreol) {
  1342.                 home ();
  1343.                 cleareol ();
  1344.                 }
  1345.                 else
  1346.                 doclear ();
  1347.             }
  1348.             else {
  1349.             kill_line ();
  1350.             if (noscroll)
  1351.                 if (clreol) {
  1352.                     home ();
  1353.                     cleareol ();
  1354.                 }
  1355.                 else
  1356.                 doclear ();
  1357.             pr (Line);
  1358.             putchar ('\n');
  1359.             }
  1360.             break;
  1361.         }
  1362.     else if (rv == -1)
  1363.         error ("Regular expression botch");
  1364.     }
  1365.     if (feof (file)) {
  1366.     if (!no_intty) {
  1367.         Currline = saveln;
  1368.         Fseek (file, startline);
  1369.     }
  1370.     else {
  1371.         pr ("\nPattern not found\n");
  1372.         end_it ();
  1373.     }
  1374.     error ("Pattern not found");
  1375.     }
  1376. }
  1377.  
  1378. /*VARARGS2*/
  1379. execute (va_alist)
  1380. va_dcl
  1381. {
  1382.     int id;
  1383.     int n;
  1384.     char *filename;
  1385.     char *cmd;
  1386.     va_list argp;
  1387.  
  1388.     va_start(argp);
  1389.     filename = va_arg(argp, char *);
  1390.     cmd = va_arg(argp, char *);
  1391.  
  1392.     fflush (stdout);
  1393.     reset_tty ();
  1394.     for (n = 10; (id = fork ()) < 0 && n > 0; n--)
  1395.         sleep (5);
  1396.     if (id == 0) {
  1397.         if (!isatty(0)) {
  1398.         close(0);
  1399.         open("/dev/tty", 0);
  1400.         }
  1401.  
  1402.         execv (cmd, argp);
  1403.         write (2, "exec failed\n", 12);
  1404.         exit (1);
  1405.     }
  1406.     if (id > 0) {
  1407.         signal (SIGINT, SIG_IGN);
  1408.         signal (SIGQUIT, SIG_IGN);
  1409.         if (catch_susp)
  1410.         signal(SIGTSTP, SIG_DFL);
  1411.         while (wait(0) > 0);
  1412.         signal (SIGINT, end_it);
  1413.         signal (SIGQUIT, onquit);
  1414.         if (catch_susp)
  1415.         signal(SIGTSTP, onsusp);
  1416.     } else
  1417.         write(2, "can't fork\n", 11);
  1418.     set_tty ();
  1419.     pr ("------------------------\n");
  1420.     prompt (filename);
  1421.     va_end(argp);    /* balance {}'s for some UNIX's */
  1422. }
  1423. /*
  1424. ** Skip n lines in the file f
  1425. */
  1426.  
  1427. skiplns (n, f)
  1428. register int n;
  1429. register FILE *f;
  1430. {
  1431.     register char c;
  1432.  
  1433.     while (n > 0) {
  1434.     while ((c = Getc (f)) != '\n')
  1435.         if (c == EOF)
  1436.         return;
  1437.         n--;
  1438.         Currline++;
  1439.     }
  1440. }
  1441.  
  1442. /*
  1443. ** Skip nskip files in the file list (from the command line). Nskip may be
  1444. ** negative.
  1445. */
  1446.  
  1447. skipf (nskip)
  1448. register int nskip;
  1449. {
  1450.     if (nskip == 0) return;
  1451.     if (nskip > 0) {
  1452.     if (fnum + nskip > nfiles - 1)
  1453.         nskip = nfiles - fnum - 1;
  1454.     }
  1455.     else if (within)
  1456.     ++fnum;
  1457.     fnum += nskip;
  1458.     if (fnum < 0)
  1459.     fnum = 0;
  1460.     pr ("\n...Skipping ");
  1461.     pr ("\n");
  1462.     if (clreol)
  1463.     cleareol ();
  1464.     pr ("...Skipping ");
  1465.     pr (nskip > 0 ? "to file " : "back to file ");
  1466.     pr (fnames[fnum]);
  1467.     pr ("\n");
  1468.     if (clreol)
  1469.     cleareol ();
  1470.     pr ("\n");
  1471.     --fnum;
  1472. }
  1473.  
  1474. /*----------------------------- Terminal I/O -------------------------------*/
  1475.  
  1476. initterm ()
  1477. {
  1478.     char    buf[TBUFSIZ];
  1479.     static char    clearbuf[TBUFSIZ];
  1480.     char    *clearptr, *padstr;
  1481.     int        ldisc;
  1482.     int        lmode;
  1483.     char    *term;
  1484.     int        tgrp;
  1485.     struct winsize win;
  1486.  
  1487. retry:
  1488.     if (!(no_tty = gtty(fileno(stdout), &otty))) {
  1489.     if (ioctl(fileno(stdout), TIOCLGET, &lmode) >= 0) {
  1490.         docrterase = ((lmode & LCRTERA) != 0);
  1491.         docrtkill = ((lmode & LCRTKIL) != 0);
  1492.         /*
  1493.          * Wait until we're in the foreground before we save the
  1494.          * the terminal modes.
  1495.          */
  1496.         if (ioctl(fileno(stdout), TIOCGPGRP, &tgrp) < 0) {
  1497.         perror("TIOCGPGRP");
  1498.         exit(1);
  1499.         }
  1500.         if (tgrp != getpgrp(0)) {
  1501.         kill(0, SIGTTOU);
  1502.         goto retry;
  1503.         }
  1504.     }
  1505.     if ((term = getenv("TERM")) == 0 || tgetent(buf, term) <= 0) {
  1506.         dumb++; ul_opt = 0;
  1507.     }
  1508.     else {
  1509.         if (ioctl(fileno(stdout), TIOCGWINSZ, &win) < 0) {
  1510.         Lpp = tgetnum("li");
  1511.         Mcol = tgetnum("co");
  1512.         } else {
  1513.         if ((Lpp = win.ws_row) == 0)
  1514.             Lpp = tgetnum("li");
  1515.         if ((Mcol = win.ws_col) == 0)
  1516.             Mcol = tgetnum("co");
  1517.         }
  1518.         if ((Lpp <= 0) || tgetflag("hc")) {
  1519.         hard++;    /* Hard copy terminal */
  1520.         Lpp = 24;
  1521.         }
  1522.         if (tgetflag("xn"))
  1523.         eatnl++; /* Eat newline at last column + 1; dec, concept */
  1524.         if (Mcol <= 0)
  1525.         Mcol = 80;
  1526.  
  1527.         if (tailequ (fnames[0], "page") || !hard && tgetflag("ns"))
  1528.         noscroll++;
  1529.         Wrap = tgetflag("am");
  1530.         bad_so = tgetflag ("xs");
  1531.         clearptr = clearbuf;
  1532.         eraseln = tgetstr("ce",&clearptr);
  1533.         Clear = tgetstr("cl", &clearptr);
  1534.         Senter = tgetstr("so", &clearptr);
  1535.         Sexit = tgetstr("se", &clearptr);
  1536.         if ((soglitch = tgetnum("sg")) < 0)
  1537.         soglitch = 0;
  1538.  
  1539.         /*
  1540.          *  Set up for underlining:  some terminals don't need it;
  1541.          *  others have start/stop sequences, still others have an
  1542.          *  underline char sequence which is assumed to move the
  1543.          *  cursor forward one character.  If underline sequence
  1544.          *  isn't available, settle for standout sequence.
  1545.          */
  1546.  
  1547.         if (tgetflag("ul") || tgetflag("os"))
  1548.         ul_opt = 0;
  1549.         if ((chUL = tgetstr("uc", &clearptr)) == NULL )
  1550.         chUL = "";
  1551.         if (((ULenter = tgetstr("us", &clearptr)) == NULL ||
  1552.              (ULexit = tgetstr("ue", &clearptr)) == NULL) && !*chUL) {
  1553.             if ((ULenter = Senter) == NULL || (ULexit = Sexit) == NULL) {
  1554.             ULenter = "";
  1555.             ULexit = "";
  1556.         } else
  1557.             ulglitch = soglitch;
  1558.         } else {
  1559.         if ((ulglitch = tgetnum("ug")) < 0)
  1560.             ulglitch = 0;
  1561.         }
  1562.  
  1563.         if (padstr = tgetstr("pc", &clearptr))
  1564.         PC = *padstr;
  1565.         Home = tgetstr("ho",&clearptr);
  1566.         if (Home == 0 || *Home == '\0')
  1567.         {
  1568.         if ((cursorm = tgetstr("cm", &clearptr)) != NULL) {
  1569.             strcpy(cursorhome, tgoto(cursorm, 0, 0));
  1570.             Home = cursorhome;
  1571.            }
  1572.         }
  1573.         EodClr = tgetstr("cd", &clearptr);
  1574.         if ((chBS = tgetstr("bc", &clearptr)) == NULL)
  1575.         chBS = "\b";
  1576.  
  1577.     }
  1578.     if ((shell = getenv("SHELL")) == NULL)
  1579.         shell = "/bin/sh";
  1580.     }
  1581.     no_intty = gtty(fileno(stdin), &otty);
  1582.     gtty(fileno(stderr), &otty);
  1583.     savetty = otty;
  1584.     ospeed = otty.sg_ospeed;
  1585.     slow_tty = ospeed < B1200;
  1586.     hardtabs = (otty.sg_flags & TBDELAY) != XTABS;
  1587.     if (!no_tty) {
  1588.     otty.sg_flags &= ~ECHO;
  1589.     if (MBIT == CBREAK || !slow_tty)
  1590.         otty.sg_flags |= MBIT;
  1591.     }
  1592. }
  1593.  
  1594. readch ()
  1595. {
  1596.     char ch;
  1597.     extern int errno;
  1598.  
  1599.     errno = 0;
  1600.     if (read (2, &ch, 1) <= 0)
  1601.         if (errno != EINTR)
  1602.             end_it();
  1603.         else
  1604.             ch = otty.sg_kill;
  1605.     return (ch);
  1606. }
  1607.  
  1608. static char BS = '\b';
  1609. static char *BSB = "\b \b";
  1610. static char CARAT = '^';
  1611. #define ERASEONECHAR \
  1612.     if (docrterase) \
  1613.     write (2, BSB, sizeof(BSB)); \
  1614.     else \
  1615.     write (2, &BS, sizeof(BS));
  1616.  
  1617. ttyin (buf, nmax, pchar)
  1618. char buf[];
  1619. register int nmax;
  1620. char pchar;
  1621. {
  1622.     register char *sptr;
  1623.     register char ch;
  1624.     register int slash = 0;
  1625.     int    maxlen;
  1626.     char cbuf;
  1627.  
  1628.     sptr = buf;
  1629.     maxlen = 0;
  1630.     while (sptr - buf < nmax) {
  1631.     if (promptlen > maxlen) maxlen = promptlen;
  1632.     ch = readch ();
  1633.     if (ch == '\\') {
  1634.         slash++;
  1635.     }
  1636.     else if ((ch == otty.sg_erase) && !slash) {
  1637.         if (sptr > buf) {
  1638.         --promptlen;
  1639.         ERASEONECHAR
  1640.         --sptr;
  1641.         if ((*sptr < ' ' && *sptr != '\n') || *sptr == RUBOUT) {
  1642.             --promptlen;
  1643.             ERASEONECHAR
  1644.         }
  1645.         continue;
  1646.         }
  1647.         else {
  1648.         if (!eraseln) promptlen = maxlen;
  1649.         longjmp (restore, 1);
  1650.         }
  1651.     }
  1652.     else if ((ch == otty.sg_kill) && !slash) {
  1653.         if (hard) {
  1654.         show (ch);
  1655.         putchar ('\n');
  1656.         putchar (pchar);
  1657.         }
  1658.         else {
  1659.         putchar ('\r');
  1660.         putchar (pchar);
  1661.         if (eraseln)
  1662.             erase (1);
  1663.         else if (docrtkill)
  1664.             while (promptlen-- > 1)
  1665.             write (2, BSB, sizeof(BSB));
  1666.         promptlen = 1;
  1667.         }
  1668.         sptr = buf;
  1669.         fflush (stdout);
  1670.         continue;
  1671.     }
  1672.     if (slash && (ch == otty.sg_kill || ch == otty.sg_erase)) {
  1673.         ERASEONECHAR
  1674.         --sptr;
  1675.     }
  1676.     if (ch != '\\')
  1677.         slash = 0;
  1678.     *sptr++ = ch;
  1679.     if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) {
  1680.         ch += ch == RUBOUT ? -0100 : 0100;
  1681.         write (2, &CARAT, 1);
  1682.         promptlen++;
  1683.     }
  1684.     cbuf = ch;
  1685.     if (ch != '\n' && ch != ESC) {
  1686.         write (2, &cbuf, 1);
  1687.         promptlen++;
  1688.     }
  1689.     else
  1690.         break;
  1691.     }
  1692.     *--sptr = '\0';
  1693.     if (!eraseln) promptlen = maxlen;
  1694.     if (sptr - buf >= nmax - 1)
  1695.     error ("Line too long");
  1696. }
  1697.  
  1698. expand (outbuf, inbuf)
  1699. char *outbuf;
  1700. char *inbuf;
  1701. {
  1702.     register char *instr;
  1703.     register char *outstr;
  1704.     register char ch;
  1705.     char temp[200];
  1706.     int changed = 0;
  1707.  
  1708.     instr = inbuf;
  1709.     outstr = temp;
  1710.     while ((ch = *instr++) != '\0')
  1711.     switch (ch) {
  1712.     case '%':
  1713.         if (!no_intty) {
  1714.         strcpy (outstr, fnames[fnum]);
  1715.         outstr += strlen (fnames[fnum]);
  1716.         changed++;
  1717.         }
  1718.         else
  1719.         *outstr++ = ch;
  1720.         break;
  1721.     case '!':
  1722.         if (!shellp)
  1723.         error ("No previous command to substitute for");
  1724.         strcpy (outstr, shell_line);
  1725.         outstr += strlen (shell_line);
  1726.         changed++;
  1727.         break;
  1728.     case '\\':
  1729.         if (*instr == '%' || *instr == '!') {
  1730.         *outstr++ = *instr++;
  1731.         break;
  1732.         }
  1733.     default:
  1734.         *outstr++ = ch;
  1735.     }
  1736.     *outstr++ = '\0';
  1737.     strcpy (outbuf, temp);
  1738.     return (changed);
  1739. }
  1740.  
  1741. show (ch)
  1742. register char ch;
  1743. {
  1744.     char cbuf;
  1745.  
  1746.     if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) {
  1747.     ch += ch == RUBOUT ? -0100 : 0100;
  1748.     write (2, &CARAT, 1);
  1749.     promptlen++;
  1750.     }
  1751.     cbuf = ch;
  1752.     write (2, &cbuf, 1);
  1753.     promptlen++;
  1754. }
  1755.  
  1756. error (mess)
  1757. char *mess;
  1758. {
  1759.     if (clreol)
  1760.     cleareol ();
  1761.     else
  1762.     kill_line ();
  1763.     promptlen += strlen (mess);
  1764.     if (Senter && Sexit) {
  1765.     tputs (Senter, 1, putch);
  1766.     pr(mess);
  1767.     tputs (Sexit, 1, putch);
  1768.     }
  1769.     else
  1770.     pr (mess);
  1771.     fflush(stdout);
  1772.     errors++;
  1773.     longjmp (restore, 1);
  1774. }
  1775.  
  1776.  
  1777. set_tty ()
  1778. {
  1779.     otty.sg_flags |= MBIT;
  1780.     otty.sg_flags &= ~ECHO;
  1781.     stty(fileno(stderr), &otty);
  1782. }
  1783.  
  1784. reset_tty ()
  1785. {
  1786.     if (no_tty)
  1787.     return;
  1788.     if (pstate) {
  1789.     tputs(ULexit, 1, putch);
  1790.     fflush(stdout);
  1791.     pstate = 0;
  1792.     }
  1793.     otty.sg_flags |= ECHO;
  1794.     otty.sg_flags &= ~MBIT;
  1795.     stty(fileno(stderr), &savetty);
  1796. }
  1797.  
  1798. rdline (f)
  1799. register FILE *f;
  1800. {
  1801.     register char c;
  1802.     register char *p;
  1803.  
  1804.     p = Line;
  1805.     while ((c = Getc (f)) != '\n' && c != EOF && p - Line < LINSIZ - 1)
  1806.     *p++ = c;
  1807.     if (c == '\n')
  1808.     Currline++;
  1809.     *p = '\0';
  1810. }
  1811.  
  1812. /* Come here when we get a suspend signal from the terminal */
  1813.  
  1814. onsusp ()
  1815. {
  1816.     /* ignore SIGTTOU so we don't get stopped if csh grabs the tty */
  1817.     signal(SIGTTOU, SIG_IGN);
  1818.     reset_tty ();
  1819.     fflush (stdout);
  1820.     signal(SIGTTOU, SIG_DFL);
  1821.     /* Send the TSTP signal to suspend our process group */
  1822.     signal(SIGTSTP, SIG_DFL);
  1823.     sigsetmask(0);
  1824.     kill (0, SIGTSTP);
  1825.     /* Pause for station break */
  1826.  
  1827.     /* We're back */
  1828.     signal (SIGTSTP, onsusp);
  1829.     set_tty ();
  1830.     if (inwait)
  1831.         longjmp (restore);
  1832. }
  1833.